home *** CD-ROM | disk | FTP | other *** search
/ Delphi Anthology / aDELPHI.iso / Runimage / Delphi50 / Source / Decision Cube / mxcommon.pas < prev    next >
Pascal/Delphi Source File  |  1999-08-11  |  34KB  |  1,247 lines

  1. {*******************************************************}
  2. {                                                       }
  3. {       Borland Delphi Visual Component Library         }
  4. {                                                       }
  5. {       Copyright (c) 1997,99 Inprise Corporation       }
  6. {                                                       }
  7. {*******************************************************}
  8.  
  9. unit mxcommon;
  10.  
  11. interface
  12.  
  13. uses
  14.   dialogs, Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  15.   inifiles, db, dbtables, dbcommon, bde, mxarrays, mxconsts;
  16.  
  17. type
  18.   EDimensionMapError = class(Exception);
  19.  
  20.   TQueryError = (tqeOK, tqeNoDimensions, tqeNoAggs, tqeNotGrouped,
  21.            tqeNotInitialized, tqeMapCorrupt, tqeUnKnownDims);
  22.  
  23.   TBinType = (binNone, binYear, binQuarter, binMonth, binSet, binCustom);
  24.   TBinTypes = set of TBinType;
  25.  
  26.   TActiveFlags = (diActive, diAsNeeded, diInactive);
  27.  
  28.   TDimFlags = (dimDimension, dimSum, dimCount, dimAverage, dimMin, dimMax, dimGenericAgg, dimUnknown);
  29.  
  30.   TCDNotifyType = (tdChanged);
  31.  
  32.   TDimensionItem = class(TCollectionItem)
  33.   private
  34.     FDerivedFrom: Integer;
  35.     FFormat: String;
  36.     FName: String;
  37.     FFieldName: String;
  38.     FBaseName: String;
  39.     FFieldType: TFieldType;
  40.     FActive: Boolean;
  41.     FActiveFlag:  TActiveFlags;
  42.     FOwner: TCollection;
  43.     FType:  TDimFlags;
  44.     procedure SetName(Value: string);
  45.     procedure SetBaseName(Value: string);
  46.     procedure SetFieldName(Value: string);
  47.     procedure SetDerivedFrom(Value: Integer);
  48.     procedure SetFormat(Value: String);
  49.     procedure SetFieldType(Value: TFieldType);
  50.     procedure SetType( Value: TDimFlags );
  51.     procedure SetActive(Value: Boolean);
  52.     procedure SetActiveFlag( Value: TActiveFlags );
  53.   protected
  54.     procedure InitializeRange; virtual;
  55.     procedure NotifyCollection(aType: TCDNotifyType); virtual;
  56.     property Active: Boolean read FActive write SetActive;
  57.     property Owner: TCollection read FOwner;
  58.   public
  59.     constructor Create(Collection: TCollection); override;
  60.     procedure Assign(Value: TPersistent); override;
  61.     function IsDimension: Boolean;
  62.     function IsSummary: Boolean;
  63.   published
  64.     property ActiveFlag: TActiveFlags read FActiveFlag write SetActiveFlag;
  65.     property Format: String read FFormat write SetFormat;
  66.     property FieldType: TFieldType read FFieldType write SetFieldType;
  67.     property Fieldname: string read Ffieldname write Setfieldname;
  68.     property BaseName: string read FBaseName write setBaseName;
  69.     property Name: string read FName write SetName;
  70.     property DerivedFrom: Integer read FDerivedFrom write SetDerivedFrom;
  71.     property DimensionType: TDimFlags read FType write SetType;
  72.   end;
  73.  
  74.   TDimensionItemClass = class of TDimensionItem;
  75.  
  76.   TDimensionItems = class(TCollection)
  77.   private
  78.     FOwner: TPersistent;
  79.     bQuiet: Boolean;
  80.     function GetDimensionItem(Index: Integer): TDimensionItem;
  81.     procedure SetDimensionItem(Index: Integer; Value: TDimensionItem);
  82.     function GetDimCount: Integer;
  83.     function GetSumCount: Integer;
  84.     function GetActiveDimCount: Integer;
  85.     function GetActiveSumCount: Integer;
  86.   protected
  87.     function GetOwner: TPersistent; override;
  88.     procedure NotifyOwner(aType: TCDNotifyType);
  89.   public
  90.     constructor Create(Owner: TPersistent; ItemClass: TDimensionItemClass);
  91.     function Add: TDimensionItem;
  92.     procedure Assign(Source: TPersistent); override;
  93.     function AddDerivedField(Index: Integer; dimType: tDimFlags): Integer;
  94.     Function AverageFieldCheck(Index: Integer; var SumIndex, CountIndex: Integer): Boolean;
  95.     function AlreadyExists(BaseName: String; sType: TDimFlags): Boolean;
  96.     property Items[Index: Integer]:TDimensionItem read GetDimensionItem write SetDimensionItem; default;
  97.     property DimensionCount: Integer read GetDimCount;
  98.     property SummaryCount: Integer read GetSumCount;
  99.     property ActiveDimensionCount: Integer read GetActiveDimCount;
  100.     property ActiveSummaryCount: Integer read GetActiveSumCount;
  101.   end;
  102.  
  103.  
  104.   PTickInfoRec = ^TTickInfoRec;
  105.   TTickInfoRec = Record
  106.     StartTicks,
  107.     EndTicks,
  108.     TotalTicks: Integer;
  109.     SName: string;
  110.   end;
  111.  
  112.   TTicks = class
  113.   private
  114.     FTicksList: TList;
  115.     FLogFile: TIniFile;
  116.     FNumValues: Integer;
  117.   public
  118.    constructor Create(FileName: string);
  119.    destructor Destroy; override;
  120.    procedure Dump(bDetail: Boolean);
  121.    procedure Clear;
  122.    procedure Ticks(SectionName: string);
  123.    procedure TicksIntArray(SectionName: string; IntArray: TIntArray);
  124.    procedure TicksSmallIntArray(SectionName: string; IntArray: TSmallIntArray);
  125.    property NumberOfValues: Integer read FNumValues write FNumValues;
  126.  end;
  127.  
  128.   TBinData = class(TPersistent)
  129.   private
  130.     FNameList: TStringList;
  131.     FValueList: TList;
  132.     FOtherStr: string;
  133.     function FindName(BinName: string; var pos: Integer): Boolean;
  134.   public
  135.     constructor create;
  136.     destructor destroy; override;
  137.     procedure Clear;
  138.     procedure Assign(Value: TPersistent); override;
  139.     procedure AddBinValues(BinName: string; const Values: array of const);
  140.     function AddBinValue(BinName: string; Value: Variant): Integer;
  141.     function BinValueCount(BinName: string): Integer;
  142.     function GetAllBinValueCount: Integer;
  143.     function GetBinValue(BinName: string; Index: Integer): Variant;
  144.     function GetBinValues(BinName: string): Variant;
  145.     function GetBinName(Index: Integer): string;
  146.     function GetBinCount: Integer;
  147.     function GetBinNameDataType: TFieldType;
  148.     function GetMaxBinNameSize: Integer;
  149.     function AddBin( BinName: string; vType: Integer ): Integer;
  150.     function AddIBinValue( iBin: Integer; value: variant): Integer;
  151.     function GetIBinValue( iBin: Integer; ValueIndex: Integer): variant;
  152.     function GetIBinValueCount( iBin: Integer): Integer;
  153.     property OtherBinName: string read FOtherStr write FOtherStr;
  154.     property BinName[ Index:Integer ]: string read GetBinName;
  155.     property BinCount: Integer read GetBinCount;
  156.     property NameList: TStringList read FNameList;
  157.     property ValueList: TList read FValueList;
  158.   end;
  159.  
  160.   function FieldTypeToVarType(DataType: TFieldType): Integer;
  161.   function VerifyRTQuery(aDataSet: TDataSet; Map: TDimensionItems; var bDataSetMatch: Boolean): TQueryError;
  162.   function BuildDataSetMap(aDataSet: TDataSet; Map: TDimensionItems; var bParsed: Boolean; var bDataSetMatch: Boolean): TQueryError;
  163.   function GetAggName(aType: TDimFlags; FieldName: string): string;
  164.   function IsAggValid(AggType: TDimFlags; FieldType: TFieldType): Boolean;
  165.   procedure AddToQuerySelect(var SQLString: string; Select: string);
  166.   function CheckIfEmptyQuery(var SQLString: string): Boolean;
  167.   procedure UpdateDesigner(Sender: TComponent);
  168.   function FormatVariant(Value: Variant; FFormat: String): string;
  169.   function NextArg(var aPos: Integer; Source: string): string;
  170.   function HookToDataBase(aDataSet: TDataSet): TDataBase;
  171.   function IsBDEAvailable: Boolean;
  172.  
  173. implementation
  174.  
  175. uses
  176.   mxqparse, mxstore;
  177.  
  178. type
  179.   THackQuery = class(TDBDataSet)
  180.   end;
  181.  
  182. function IsBDEAvailable: Boolean;
  183. var
  184.   Status: DBIResult;
  185.   Env: DbiEnv;
  186. begin
  187.   Result := Session.Active;
  188.   if (Result = False) then
  189.   begin
  190.     FillChar(Env, SizeOf(Env), 0);
  191.     StrPLCopy(Env.szLang, SIDAPILangID, SizeOf(Env.szLang) - 1);
  192.     Status := DbiInit(@Env);
  193.     if (Status = DBIERR_CANTLOADIDAPI) then Result := False;
  194.   end;
  195. end;
  196.  
  197. function GetAggName(aType: TDimFlags; FieldName: string): string;
  198. begin
  199.   Result := FieldName;
  200.   case aType of
  201.     dimSum     : Result := Format(sSumLabel, [Result]);
  202.     dimCount   : Result := Format(sCountLabel, [Result]);
  203.     dimAverage : Result := Format(sAverageLabel, [Result]);
  204.     dimMax     : Result := Format(sMaxLabel, [Result]);
  205.     dimMin     : Result := Format(sMinLabel, [Result]);
  206.     else
  207.       Result := Format(sAggLabel, [Result]);
  208.    end;
  209. end;
  210.  
  211. function FieldTypeToVarType( DataType: TFieldType ): Integer;
  212. begin
  213.   Result := FieldTypeVarMap[DataType];
  214. end;
  215.  
  216. { DataCube Collection Definition }
  217.  
  218. constructor TDimensionItems.Create(Owner: TPersistent; ItemClass: TDimensionItemClass);
  219. begin
  220.   inherited Create(ItemClass);
  221.   FOwner := Owner;
  222.   bQuiet := True;
  223. end;
  224.  
  225. function TDimensionItems.GetOwner: TPersistent;
  226. begin
  227.   Result := FOwner;
  228. end;
  229.  
  230. function TDimensionItems.GetDimensionItem(Index: Integer): TDimensionItem;
  231. begin
  232.   Result := TDimensionItem(inherited Items[Index]);
  233. end;
  234.  
  235. procedure TDimensionItems.NotifyOwner(aType: TCDNotifyType);
  236. begin
  237.   if bQuiet then Exit;
  238. end;
  239.  
  240. procedure TDimensionItems.SetDimensionItem(Index: Integer; Value: TDimensionItem);
  241. begin
  242.   Items[Index].Assign(Value);
  243. end;
  244.  
  245. function TDimensionItems.Add: TDimensionItem;
  246. begin
  247.   Result := TDimensionItem(inherited Add);
  248. end;
  249.  
  250. procedure TDimensionItems.Assign(Source: TPersistent);
  251. begin
  252.   inherited;
  253. end;
  254.  
  255. function TDimensionItems.GetDimCount: Integer;
  256. var
  257.   I: Integer;
  258. begin
  259.   Result := 0;
  260.   for I := 0 to Count-1 do
  261.     if Items[I].IsDimension then
  262.       Inc(Result);
  263. end;
  264.  
  265. function TDimensionItems.GetSumCount: Integer;
  266. var
  267.   I: Integer;
  268. begin
  269.   Result := 0;
  270.   for I := 0 to Count-1 do
  271.     if Items[I].IsSummary then
  272.       Inc(Result);
  273. end;
  274.  
  275. function TDimensionItems.GetActiveDimCount: Integer;
  276. var
  277.   I: Integer;  
  278. begin
  279.   Result := 0;  
  280.   for I := 0 to Count-1 do
  281.     if (Items[I].IsDimension) and (Items[i].Active) then
  282.       Inc(Result);
  283. end;
  284.  
  285. function TDimensionItems.GetActiveSumCount: Integer;
  286. var
  287.   I: Integer;  
  288. begin
  289.   Result := 0;  
  290.   for I := 0 to Count-1 do
  291.     if (Items[I].IsSummary) and (Items[I].Active) then
  292.       Inc(Result);
  293. end;
  294.  
  295. function TDimensionItems.AlreadyExists(BaseName: String; sType: TDimFlags): Boolean;
  296. var
  297.   i: Integer;  
  298. begin
  299.   Result := False;
  300.   for i := 0 to self.count-1 do
  301.   begin
  302.     if (BaseName = self[i].BaseName) and (sType = self[i].DimensionType) then
  303.     begin
  304.       Result := True;
  305.       break;
  306.     end;
  307.   end;
  308. end;
  309.  
  310. function TDimensionItems.AddDerivedField(Index: Integer; dimType: tDimFlags): Integer;
  311. var
  312.   SumIndex, CountIndex: Integer;
  313.   NewItem: TDimensionItem;  
  314. begin
  315.   Result := -1;
  316.   if not AverageFieldCheck(index, SumIndex, CountIndex) then Exit;    
  317.   NewItem := Add;
  318.   NewItem.index := Count-1;
  319.   NewItem.active := True;
  320.   NewItem.DimensionType := dimAverage;
  321.   NewItem.Name := GetAggName(dimAverage, Items[index].BaseName);
  322.   NewItem.FieldName := NewItem.Name;
  323.   NewItem.DerivedFrom := Index;
  324.   NewItem.FBaseName := Items[index].BaseName;
  325.   NewItem.FActiveFlag := diAsNeeded;
  326.   Result := NewItem.index;
  327. end;
  328.  
  329. Function TDimensionItems.AVerageFieldCheck(Index: Integer; var SumIndex, CountIndex: Integer): Boolean;
  330. var
  331.   i: Integer;
  332.   aName: string; 
  333. begin
  334.   Result := False;
  335.   if Index >= Count then Exit;   
  336.   SumIndex := -1;
  337.   CountIndex := -1;
  338.   aName := Items[index].BaseName;  
  339.   if (Items[Index].DimensionType = dimSum) then
  340.     SumIndex := Index
  341.   else if (Items[Index].DimensionType = dimCount) then
  342.     CountIndex := Index;
  343.   if (CountIndex < 0) then
  344.   begin
  345.     for i := 0 to Count-1 do
  346.     begin
  347.       if (Items[i].DimensionType = dimCount) then
  348.       begin
  349.         if (aName = Items[i].BaseName) then
  350.         begin
  351.           CountIndex := i;
  352.         end
  353.         else if (AnsiUpperCase(Items[i].Name) = sCountStar) then
  354.         begin
  355.           CountIndex := i;
  356.           break;
  357.         end;
  358.       end;
  359.     end;
  360.   end;
  361.   if (SumIndex < 0) then
  362.   begin
  363.     for i := 0 to Count-1 do
  364.     begin
  365.       if (aName = Items[i].BaseName) then
  366.       begin
  367.         if (Items[i].DimensionType = dimSum) then
  368.         begin
  369.           SumIndex := i;
  370.           break;
  371.         end;
  372.       end;
  373.     end;
  374.   end;
  375.   if (CountIndex = -1) or (SumIndex = -1) then
  376.   begin
  377.     Exit;
  378.   end;
  379.   Result := True;
  380. end;
  381.  
  382.   { TDimensionItem }
  383.  
  384. constructor TDimensionItem.Create(Collection: TCollection);
  385. begin
  386.   inherited Create(Collection);
  387.   FOwner := Collection;
  388.   FName := '';
  389.   FFormat := '';
  390.   FActive := False;
  391.   Ffieldname := '';
  392.   FDerivedFrom := -1;
  393.   FType := dimDimension;
  394.   FActiveFlag := diAsNeeded;
  395. end;
  396.  
  397. procedure TDimensionItem.assign(Value: TPersistent);
  398. begin
  399.   if (Value is TDimensionItem) then
  400.   begin
  401.     FName := TDimensionItem(Value).FName;
  402.     FFormat := TDimensionItem(Value).FFormat;
  403.     FActive := TDimensionItem(Value).FActive;
  404.     Ffieldname := TDimensionItem(Value).FFieldName;
  405.     FDerivedFrom := TDimensionItem(Value).FDerivedFrom;
  406.     FType := TDimensionItem(Value).FType;
  407.     FFieldTYpe := TDimensionItem(Value).FFieldtype;
  408.     FBaseName := TDimensionItem(Value).FBaseName;
  409.     FActiveFlag := TDimensionItem(Value).FActiveFlag;
  410.   end;
  411. end;
  412.  
  413. procedure TDimensionItem.SetName(Value: string);
  414. begin
  415.   fName := Value;
  416.   NotifyCollection(tdChanged);
  417. end;
  418.  
  419. procedure TDimensionItem.SetBaseName(Value: string);
  420. begin
  421.   fBaseName := Value;
  422.   NotifyCollection(tdChanged);
  423. end;
  424.  
  425. procedure TDimensionItem.SetFieldName(Value: string);
  426. begin
  427.   fFieldName := Value;
  428.   NotifyCollection(tdChanged);
  429. end;
  430.  
  431. procedure TDimensionItem.SetDerivedFrom(Value: Integer);
  432. begin
  433.   fDerivedFrom := Value;
  434.   NotifyCollection(tdChanged);
  435. end;
  436.  
  437. procedure TDimensionItem.SetFormat(Value: String);
  438. begin
  439.   FFormat := Value;
  440.   NotifyCollection(tdChanged);
  441. end;
  442.  
  443. procedure TDimensionItem.SetFieldType(Value: TFieldType);
  444. begin
  445.   FFieldType := Value;
  446.   NotifyCollection(tdChanged);
  447. end;
  448.  
  449. procedure TDimensionItem.SetType(Value: TDimFlags);
  450. begin
  451.   FType := Value;
  452.   NotifyCollection(tdChanged);
  453. end;
  454.  
  455. procedure TDimensionItem.SetActive(Value: Boolean);
  456. begin
  457.   FActive := Value;
  458.   NotifyCollection(tdChanged);
  459. end;
  460.  
  461. procedure TDimensionItem.SetActiveFlag(Value: TActiveFlags);
  462. begin
  463.   if (FActiveFlag <> Value) then
  464.   begin
  465.     FActiveFlag := Value;
  466.     NotifyCollection(tdChanged);
  467.   end;
  468.   FActive := (FActiveFlag = diActive);
  469. end;
  470.  
  471.  
  472. procedure TDimensionItem.InitializeRange;
  473. begin
  474. end;
  475.  
  476. procedure TDimensionItem.NotifyCollection(aType: TCDNotifyType);
  477. begin
  478.   TDimensionItems(FOwner).NotifyOwner(aType);
  479. end;
  480.  
  481. Function TDimensionItem.IsDimension: Boolean;
  482. begin
  483.   Result := (FType = dimDimension);
  484. end;
  485.  
  486. Function TDimensionItem.IsSummary: Boolean;
  487. begin
  488.   Result := (FType <> dimDimension);
  489. end;
  490.  
  491. { TTicks }
  492.  
  493. constructor TTicks.Create(FileName: string);
  494. begin
  495.   inherited Create;
  496.   FNumValues := 0;
  497.   FLogFile := TIniFile.Create(FileName);
  498.   FTicksList := TList.Create;
  499. end;
  500.  
  501. destructor TTicks.Destroy;
  502. begin
  503.   Dump(False);
  504.   Clear;
  505.   FLogFile.Free;
  506.   inherited destroy;
  507. end;
  508.  
  509. procedure TTicks.Clear;
  510. var
  511.   tRec: PTickInfoRec;
  512. begin
  513.   if Assigned(FTicksList) then
  514.   begin
  515.     while (FTicksList.Count > 0) do
  516.     begin
  517.       tRec := FTicksList.Last;
  518.       FTicksList.Remove(tRec);
  519.       Dispose(tRec);
  520.     end;
  521.     FTicksList.Free;
  522.   end;
  523. end;
  524.  
  525. procedure TTicks.Ticks(SectionName: string);
  526. var
  527.   I: Integer;
  528.   t: LongInt;
  529.   tRec: PTickInfoRec;
  530.   bFound: Boolean;
  531. begin
  532.   t := GetTickCount;
  533.   tRec := nil;
  534.   bFound := False;  
  535.   for I := 0 to FTicksList.Count-1 do
  536.   begin
  537.     tRec := FTicksList[I];
  538.     if (tRec.sName = SectionName) then
  539.     begin
  540.       bFound := True;
  541.       break;
  542.     end;
  543.   end;
  544.   if bFound then
  545.   begin
  546.     if (tRec.EndTicks = 0) then
  547.       tRec.EndTicks := t;
  548.     tRec.TotalTicks := tRec.EndTicks - tRec.StartTicks;
  549.   end
  550.   else
  551.   begin
  552.     New(tRec);
  553.     tRec.sName := SectionName;
  554.     tRec.StartTicks := t;
  555.     tRec.EndTicks := 0;
  556.     tRec.TotalTicks := 0;
  557.     FTicksList.Add(tRec);
  558.   end;
  559. end;
  560.  
  561. procedure TTicks.TicksIntArray(SectionName: string; IntArray: TIntArray);
  562. var
  563.   I: Integer;
  564.   name: string;
  565. begin
  566.   for I := 0 to IntArray.Count-1 do
  567.     name := name + ';' + IntToStr(IntArray[I]);
  568.   name := SectionName + name;
  569.   Ticks(name);
  570. end;
  571.  
  572. procedure TTicks.TicksSmallIntArray(SectionName: string; IntArray: TSmallIntArray);
  573. var
  574.   I: Integer;
  575.   name: string;
  576. begin
  577.   for I := 0 to IntArray.Count-1 do
  578.     name := name + ';' + IntToStr(IntArray[I]);
  579.   name := SectionName + name;
  580.   Ticks(name);
  581. end;
  582.  
  583. procedure TTicks.Dump(bDetail: Boolean);
  584. var
  585.   I, SummaryTicks: Integer;
  586.   tRec: PTickInfoRec;
  587. begin
  588.   SummaryTicks := 0;
  589.   for I := 0 to FTicksList.Count-1 do
  590.   begin
  591.     tRec := FTicksList[I];
  592.     if not bDetail then
  593.     begin
  594.       if Pos('SummaryAs',  tRec.sName) > 0 then
  595.       begin
  596.         SummaryTicks := SummaryTicks + tRec.TotalTicks;
  597.         Continue;
  598.       end;
  599.     end;
  600.     FLogFile.WriteInteger(tRec.sName, 'TicksInMilliSeconds', tRec.TotalTicks);
  601.   end;
  602.   if not bDetail then
  603.   begin
  604.     FLogFile.WriteInteger('TotalGetSummaryAsString', 'TicksInMilliSeconds', SummaryTicks);
  605.     { Dump number of cells : TotalSparseValues, TotalNonSparseValues }
  606.     FLogFile.WriteInteger('TotalValues', 'NumberOfValues', FNumValues);
  607.   end;
  608. end;
  609.  
  610. {
  611.   Simply reports if the logical data set ( Changed via Field mapping )
  612.   matchs the query projection or the physical data set ( TTable, TClientDataSet )
  613. }
  614.  
  615. function LogicalDataSetMatch(aDataSet: TDataset; myQuery: TXTAbQuery): Boolean;
  616. var
  617.   I: Integer;
  618.   CursorProps: CurProps;
  619.   Cursor: HDBICur;
  620. begin
  621.   Result := True;
  622.   myQuery := nil;
  623.   if assigned(myQuery) then
  624.   begin
  625.     if (myQuery.isLegal = tqenotInitialized) then
  626.       Result := False;
  627.       { Check the order }
  628.     if (Result = True) then
  629.     begin
  630.       for I := 0 to aDataSet.FieldCount-1 do
  631.       begin
  632.         if (aDataSet.Fields[I].FieldName <> myQuery.Projector[I].OutputName) then
  633.         begin
  634.           Result := False;
  635.           break;
  636.         end;
  637.       end;
  638.     end;
  639.     { Check the count }
  640.     if (Result = True) then
  641.     begin
  642.       if (myQuery.NProjectors <> aDataSet.FieldCount) then
  643.         Result := False;
  644.     end;
  645.   end
  646.   else
  647.   begin
  648.     if (aDataSet is TTable) or (aDataSet is TQuery) then
  649.     begin
  650.       { Get the field count from the table }
  651.       Cursor := TDBDataSet( aDataSet ).Handle;
  652.       DbiGetCursorProps( Cursor, CursorProps );
  653.       if (aDataSet.FieldCount <> CursorProps.iFields) then
  654.         Result := False;
  655.     end;
  656.   end;
  657. end;
  658.  
  659. function BuildMap(aDataSet: TDataset; Map: TDimensionItems; var bParsed: Boolean;
  660.                   var bDataSetMatch: Boolean): TQueryError;
  661. var
  662.   j,ci,si,k: Integer;
  663.   myQuery: TXTabQuery;
  664.   bFound: Boolean;
  665.   NewItem: TDimensionItem;
  666.   x: Integer;
  667.   new: Integer;
  668.   aString: string;
  669. begin
  670.   bParsed := False;
  671.   Result := tqeNotInitialized;
  672.   if not assigned(Map) then Exit;
  673.   {
  674.     first see if a Query parse can be done for this dataset.  If not, don't
  675.     fail, but rely on the assignments in the Map
  676.   }
  677.   myQuery := nil;
  678.   if (aDataSet is TQuery) then
  679.   begin
  680.     try
  681.       myQuery := TXtabQuery.create;
  682.       myQuery.DBHandle := TQuery(aDataSet).Database.Handle;
  683.       myQuery.canDelete := False;
  684.       myQuery.SQLString := TQuery(aDataSet).SQL.Text;
  685.     except
  686.       on e: exception do
  687.       begin
  688.         aString := e.message;
  689.         myQuery.Free;
  690.         myQuery := nil;
  691.       end;
  692.     end;
  693.   end;
  694.   bParsed := assigned(myQuery);
  695.   try
  696.     if assigned(myQuery) then
  697.     begin
  698.       Result := myQuery.isLegal;
  699.     end
  700.     else
  701.       Result := tqeOK;
  702.     {
  703.       If the query is an OK crosstab query, fix up the dimension map to place
  704.       the map in the same order as the query.  If the map was built before, this
  705.       will simply be reshuffling.  If not, all or part of the map may need to be
  706.       created.
  707.     }
  708.     if (Result <> tqenotInitialized) then
  709.     begin
  710.       for j := 0 to aDataSet.fieldCount-1 do
  711.       begin
  712.         bFound := False;
  713.         x := aDataSet.Fields[j].FieldNo-1;
  714.         {
  715.           First try to match against an existing map item.  Either the dataset field
  716.           name must match, or the type, comparename, and outputname of Randy's parse
  717.         }
  718.         if (j < Map.count) then for k := j to Map.count-1 do
  719.         begin
  720.       if (Map[k].FieldName <> aDataSet.Fields[j].FieldName) then
  721.           begin
  722.             if assigned(myQuery) and (x >= 0) then
  723.             begin
  724.               if (Map[k].FieldName <> myQuery.Projector[x].CompareName) then
  725.                 Continue;
  726.             end
  727.             else
  728.               Continue;
  729.           end;
  730.           if assigned(myQuery) and (x >= 0) then
  731.           begin
  732.             if (Map[k].DimensionType <> myQuery.Projector[x].projType) then
  733.               Continue;
  734.           end;
  735.           bFound := True;
  736.       if (j <> k) then Map[k].index := j;    { match found }
  737.           break;
  738.         end;
  739.         { if not found, set up a new map with the defaults. }
  740.         if not bFound then
  741.         begin
  742.       NewItem := Map.Add;
  743.           if (NewItem.index <> j) then NewItem.Index := j;
  744.           Map[j].active := False;
  745.           Map[j].DimensionType := dimUnknown;   { don't know until it's typed }
  746.         end;
  747.         {
  748.           Now set the fieldname and datatype from the dataset field array
  749.           If possible, set the dimension type from Randy's parse
  750.         }
  751.        if (Map[j].Name = '') then
  752.           Map[j].Name := aDataSet.Fields[j].FieldName;
  753.         Map[j].FieldName := aDataSet.Fields[j].FieldName;
  754.         Map[j].FFieldType := aDataSet.Fields[j].DataType;
  755.         if not bFound then Map[j].InitializeRange;
  756.         if assigned(myQuery) and (x >= 0) then
  757.         begin
  758.           Map[j].DimensionType := MyQuery.Projector[x].ProjType;
  759.           Map[j].BaseName := MyQuery.Projector[x].BaseName;
  760.         end;
  761.       end;
  762.     end;
  763.     j := Map.Count-1;
  764.     { Cleanout the map if entries are not in the dataset }
  765.     for k := j downto aDataSet.FieldCount do
  766.     begin
  767.       if Map[k].DerivedFrom < 0 then
  768.         Map[k].free;
  769.     end;
  770.     j := Map.Count-1;
  771.     { Remove derived fields which no longer apply }
  772.     for k := j downto aDataSet.FieldCount do
  773.     begin
  774.       if (map[k].DimensionType = dimAverage) and (map[k].DerivedFrom >= 0) then
  775.       begin
  776.         if Map.AverageFieldCheck(k,si,ci) then
  777.         begin
  778.           Map[k].DerivedFrom := si; { update derived from if changed }
  779.           Map[k].active := Map[si].active and Map[ci].active;
  780.           Continue;
  781.     end;
  782.       end;
  783.       Map[k].free;
  784.     end;
  785.     for k := 0 to aDataSet.FieldCount-1 do
  786.     begin
  787.       if (Map[k].DimensionType = dimSum) then
  788.       begin
  789.         if not Map.AverageFieldCheck(k, si, ci) then Continue;
  790.         if (Map.AlreadyExists(Map[k].BaseName, dimAverage)) then Continue;
  791.         new := Map.AddDerivedField(k, dimAverage);
  792.     Map[new].active := map[si].active and map[ci].active;
  793.       end;
  794.     end;
  795.   finally
  796.     begin
  797.       bDataSetMatch := LogicalDataSetMatch(aDataSet, myQuery);
  798.       myQuery.free;
  799.     end;
  800.   end;
  801. end;
  802.  
  803. function isMapLegal(Map: TDimensionItems): TQueryError;
  804. var
  805.   bUnknowns, bSums, bDims: Boolean;
  806.   i: Integer;
  807. begin
  808.   Result := tqeOK;
  809.   bUnknowns := False;
  810.   bSums := False;
  811.   bDims := False;
  812.   for i := 0 to Map.count-1 do
  813.   begin
  814.     if (Map[i].activeFlag <> diInactive) then
  815.     begin
  816.       if (Map[i].DimensionType = dimDimension) then
  817.         bDims := True
  818.       else if (Map[i].DimensionType = dimUnknown) then
  819.         bUnknowns := True
  820.       else
  821.         bSums := True;
  822.     end;
  823.   end;
  824.   if bUnknowns then
  825.     Result := tqeUnknownDims
  826.   else if not bDims then
  827.     Result := tqeNoDimensions
  828.   else if not bSums then
  829.     Result := tqeNoAggs;
  830. end;
  831. {
  832.   VerityRTQuery works on an active dataset (it will not open a Database)
  833.   As a final pass through the database, it forces the dimension map to be
  834.   in the same order as the executing query, and checks for validity and
  835.   name matching.  If parse info is available, it also sets agg types
  836. }
  837. function VerifyRTQuery(aDataSet: TDataSet; Map: TDimensionItems; var bDataSetMatch: Boolean): TQueryError;
  838. var
  839.   bParsed: Boolean;
  840.  
  841.   function Min(X, Y: Integer): Integer;
  842.   begin
  843.     Result := X;
  844.     if X > Y then Result := Y;
  845.   end;
  846.  
  847. begin
  848.   { Build or modify the dimmap based on the current data set }
  849.   Result := BuildDataSetMap( aDataSet, Map, bParsed, bDataSetMatch);
  850.   { Check to see if we have the minimum requirements for a valid map }
  851.   if (Result = tqeOK) then Result := isMapLegal(Map);
  852. end;
  853.  
  854. function BuildDataSetMap(aDataSet: TDataset; Map: TDimensionItems; var bParsed: Boolean; var bDataSetMatch: Boolean): TQueryError;
  855. var
  856.   wasActive: Boolean;
  857. begin
  858.   wasActive := aDataSet.active;
  859.   try
  860.     if not aDataSet.active then
  861.       THackQuery(aDataSet).opencursor(False);
  862.     Result := BuildMap(aDataSet, Map, bParsed, bDataSetMatch);
  863.   finally
  864.     if not wasActive then
  865.       THackQuery(aDataSet).CloseCursor;
  866.   end;
  867. end;
  868.  
  869. function IsAggValid(AggType: TDimFlags; FieldType: tFieldType): Boolean;
  870. begin
  871.   case AggType of
  872.     dimDimension:
  873.     begin
  874.       Result := not (FieldType in [ftBlob, ftBytes, ftUnknown]);
  875.     end;
  876.     dimCount: Result := True;
  877.     dimGenericAgg, dimUnknown: Result := False;
  878.     dimSum, dimAverage:
  879.     begin
  880.       Result := FieldType in [ftSmallint, ftInteger, ftWord, ftBoolean, ftFloat,
  881.                               ftCurrency, ftBCD, ftAutoInc, ftDateTime, ftDate, ftTime];
  882.     end;
  883.     else
  884.     begin
  885.       Result := FieldType in [ftString, ftSmallint, ftInteger, ftWord, ftFloat,
  886.                               ftCurrency, ftBCD, ftDate, ftTime, ftDateTime, ftAutoInc];
  887.     end;
  888.   end;
  889. end;
  890.  
  891. procedure AddToQuerySelect(var SQLString: string; Select: string);
  892. var
  893.   aString: string;
  894.   aPos, bPos, i: Integer;
  895.   bAdd: Boolean;
  896. begin
  897.   bAdd := False;
  898.   aString := AnsiUpperCase(SQLString);
  899.   aPos := Pos('SELECT',aString);
  900.   bPos := Pos('FROM',aString);  
  901.   if (aPos <= 0) or (bPos <= 0) or (aPos>bPos) then
  902.     raise exception.createRes(@sSelectFromError);    
  903.   Select := ' ' + Select;
  904.   for i := (aPos + 6) to bPos-1 do
  905.   begin
  906.     if ord(aString[i]) > 32 then
  907.     begin
  908.       bAdd := True;
  909.       break;
  910.     end;
  911.   end;
  912.   if bAdd then
  913.     Select := Select + ','
  914.   else
  915.     Select := Select + ' ';
  916.   Insert(Select,SQLString, aPos+6);
  917. end;
  918.  
  919. function CheckIfEmptyQuery(var SQLString: string): Boolean;
  920. var
  921.   aString: string;
  922.   aPos, bPos, i: Integer;
  923. begin
  924.   Result := False;
  925.   aString := AnsiUpperCase(SQLString);
  926.   aPos := Pos('SELECT',aString); { intl ok }
  927.   bPos := Pos('FROM',aString);   { intl ok }
  928.   if (aPos <= 0) or (bPos <= 0) or (aPos > bPos) then
  929.   begin
  930.     raise exception.createRes(@sSelectFromError);
  931.   end
  932.   else for i := (aPos + 6) to bPos-1 do
  933.   begin
  934.     if ord(aString[i]) > 32 then Exit;
  935.   end;
  936.   Result := True;
  937. end;
  938.  
  939. procedure UpdateDesigner(Sender: TComponent);
  940. var
  941.   NextParent: TComponent;
  942. begin
  943.   if (csDesigning in Sender.ComponentState) and not (csUpdating in Sender.ComponentState) then
  944.   begin
  945.     NextParent := Sender;
  946.     while assigned(NextParent) and not (NextParent is TCustomForm) do
  947.       NextParent := NextParent.Owner;
  948.     if Assigned(NextParent) and Assigned(TCustomForm(NextParent).Designer) then
  949.     begin
  950.       TCustomForm(NextParent).Designer.Modified;
  951.     end;
  952.   end;
  953. end;
  954.  
  955. function FormatVariant(Value: Variant; FFormat: String): string;
  956. var
  957.   VarData: TVarData;
  958. begin
  959.   VarData := TVarData(Value);
  960.   case TVarData(Value).vType of
  961.     varDouble   : Result := FormatFloat(FFormat, Value);
  962.     varCurrency : Result := FormatCurr(FFormat, Value);
  963.     varDate     : Result := FormatDateTime(FFormat, Value);
  964.     varInteger  : Result := FormatFloat(FFormat, Value);
  965.     else
  966.        Result := Value;
  967.   end;
  968. end;
  969.  
  970. function NextArg(var aPos: Integer; Source: string): string;
  971. var
  972.   iStart, iCount, ilen: Integer;
  973. begin
  974.   ilen := Length(Source);
  975.   Result := '';
  976.   while (aPos < iLen+1) and (ord(Source[aPos]) <= 32) do
  977.     aPos := aPos + 1;
  978.   if (aPos > iLen) then
  979.   begin
  980.     aPos := -1;  { end found }
  981.     Exit;
  982.   end;
  983.   iStart := aPos;
  984.   while ((aPos < iLen+1) and (Source[aPos] <> ',')) do
  985.     aPos := aPos + 1;
  986.   iCount := aPos - iStart;
  987.   aPos := aPos+1;
  988.   while Source[iStart + iCount] = ' ' do
  989.     iCount := iCount - 1;
  990.   Result := Copy(Source, iStart, iCount);
  991. end;
  992.   { TBinData }
  993.  
  994. function ConvertToVariant(const Value: TVarRec): Variant;
  995. begin
  996.   with Value do
  997.     case VType of
  998.       vtInteger    : Result := VInteger;
  999.       vtBoolean    : Result := VBoolean;
  1000.       vtChar       : Result := VChar;
  1001.       vtExtended   : Result := VExtended^;
  1002.       vtString     : Result := VString^;
  1003.       vtPChar      : Result := VPChar^;
  1004.       vtAnsiString : Result := string(VAnsiString);
  1005.       vtCurrency   : Result := VCurrency^;
  1006.       vtVariant    : if not VarIsEmpty(VVariant^) then
  1007.                        Result := VVariant^;
  1008.       else
  1009.         EUnsupportedTypeError.CreateResFmt(@sUnsupportedVarType, [Value.VType]); 
  1010.     end;
  1011. end;
  1012.  
  1013. constructor TBinData.Create;
  1014. begin
  1015.   inherited Create;
  1016.   FNameList  := TStringList.Create;
  1017.   FValueList := TList.Create;
  1018.   FOtherStr  := sOtherValues;
  1019. end;
  1020.  
  1021. destructor TBinData.destroy;
  1022. var
  1023.   custAr: TCustomArray;
  1024. begin
  1025.   if Assigned(FValueList) then
  1026.   begin
  1027.     while (FValueList.Count > 0) do
  1028.     begin
  1029.       custAr := FValueList.Last;
  1030.       FValueList.Remove(custAr);
  1031.       custAr.Free;
  1032.     end;
  1033.     FValueList.Free;
  1034.     FValueList := nil;
  1035.   end;
  1036.   FNameList.Free;
  1037.   FNameList := nil;
  1038.   inherited Destroy;
  1039. end;
  1040.  
  1041. procedure TBinData.Assign(Value: TPersistent);
  1042. var
  1043.   custAr, newCustAr: TCustomArray;
  1044.   I: Integer;
  1045. begin
  1046.   Clear;
  1047.   FNameList.Assign(TBinData(Value).FNameList);
  1048.   FOtherStr := TBinData(Value).FOtherStr;
  1049.   for I := 0 to TBinData(Value).FValueList.Count-1 do
  1050.   begin
  1051.     custAr := TBinData(Value).FValueList[I];
  1052.     newCustAr := TCustomArray.Create(custAr.MemberCount, custAr.DataType);
  1053.     newCustAr.Assign(custAr, False, False);
  1054.     FValueList.Add(newCustAr);
  1055.   end;
  1056. end;
  1057.  
  1058. function TBinData.AddBinValue( BinName: string; Value: Variant ): Integer;
  1059. var
  1060.   custAr: TCustomArray;
  1061.   pos: Integer;
  1062. begin
  1063.   { Add the bin name if needed, otherwise get the position of the bin name in the string list }
  1064.   if not FindName(BinName, pos) then
  1065.   begin
  1066.     pos := FNameList.add(BinName);
  1067.     custAr := TCustomArray.Create(1, VarType(Value));
  1068.     custAr.Duplicates := dupIgnore;
  1069.     custAr.Sorted := True;
  1070.     if custAr <> nil then
  1071.       FValueList.Add(custAr);
  1072.   end;
  1073.   { Get the value array }
  1074.   custAr := FValueList[pos];
  1075.   Result := custAr.Add(Value);
  1076. end;
  1077.  
  1078. procedure TBinData.AddBinValues(BinName: string; const Values: array of const);
  1079. var
  1080.   I: Integer;
  1081. begin
  1082.   for I := 0 to High(Values) do
  1083.     AddBinValue(BinName, ConvertToVariant(Values[I]));
  1084. end;
  1085.  
  1086. function TBinData.BinValueCount(BinName: string): Integer;
  1087. var
  1088.   pos: Integer;
  1089.   custAr: TCustomArray;
  1090. begin
  1091.   Result := 0;  
  1092.   if FindName(BinName, pos) then
  1093.   begin
  1094.     custAr := FValueList[pos];
  1095.     Result := custAr.MemberCount;
  1096.   end;
  1097. end;
  1098.  
  1099. function TBinData.GetAllBinValueCount: Integer;
  1100. var
  1101.   I : Integer;
  1102. begin
  1103.   Result := 0;
  1104.   for i := 0 to GetBinCount-1 do
  1105.     Result := Result + GetIBinValueCount(i);
  1106. end;
  1107.  
  1108. function TBinData.GetBinValue(BinName: string; Index: Integer): Variant;
  1109. var
  1110.   pos: Integer;
  1111.   custAr: TCustomArray;
  1112. begin
  1113.   if FindName(BinName, pos) then
  1114.   begin
  1115.     custAr := FValueList[pos];
  1116.     Result := custAr[Index];
  1117.   end;
  1118. end;
  1119.  
  1120. function TBinData.GetBinValues(BinName: string): Variant;
  1121. var
  1122.   pos: Integer;
  1123.   custAr: TCustomArray;
  1124.   I: Integer;
  1125. begin
  1126.   if FindName(BinName, pos) then
  1127.   begin
  1128.     custAr := FValueList[pos];
  1129.     Result := VarArrayCreate([0, custAr.MemberCount-1], varVariant);
  1130.     for I := 0 to custAr.MemberCount-1 do
  1131.       Result[I] := custAr[I];
  1132.   end;
  1133. end;
  1134.  
  1135. function TBinData.GetBinName(Index: Integer): string;
  1136. begin
  1137.   Result := FNameList[Index];
  1138. end;
  1139.  
  1140. function TBinData.GetBinCount: Integer;
  1141. begin
  1142.   Result := FNameList.Count;
  1143. end;
  1144.  
  1145. function TBinData.GetBinNameDataType: TFieldType;
  1146. begin
  1147.   Result := ftString;
  1148. end;
  1149.  
  1150.  
  1151. function TBinData.GetMaxBinNameSize: Integer;
  1152. var
  1153.   I : Integer;
  1154.  
  1155.   function Max(X, Y: Integer): Integer;
  1156.   begin
  1157.     Result := Y;
  1158.     if (X > Y) then Result := X;
  1159.   end;
  1160.  
  1161. begin
  1162.   Result := Length(FOtherStr);
  1163.   for I := 0 to FNameList.Count-1 do
  1164.     Result := Max(Result , Length(FNameList[I]));
  1165. end;
  1166.  
  1167. procedure TBinData.Clear;
  1168. var
  1169.   custAr: TCustomArray;
  1170. begin
  1171.   if Assigned(FValueList) then
  1172.   begin
  1173.     while (FValueList.Count > 0) do
  1174.     begin
  1175.       custAr := FValueList.Last;
  1176.       FValueList.Remove(custAr);
  1177.       custAr.Free;
  1178.     end;
  1179.   end;  
  1180.   FNameList.Clear;
  1181. end;
  1182.  
  1183. function TBinData.AddBin(BinName: string; vType: Integer): Integer;
  1184. var
  1185.   custAr: TCustomArray;
  1186.   pos: Integer;  
  1187. begin
  1188.   { Add the bin name if needed, otherwise get the position of the bin name in the string list }
  1189.   if not FindName(BinName, pos) then
  1190.   begin
  1191.     pos := FNameList.add(BinName);
  1192.     custAr := TCustomArray.Create(0, VType);
  1193.     if (custAr <> nil) then FValueList.Add(custAr);
  1194.   end;
  1195.   Result := pos;
  1196. end;
  1197.  
  1198. function TBinData.FindName(BinName: string; var pos: Integer): Boolean;
  1199. var
  1200.   i: Integer;
  1201. begin
  1202.   Result := False;
  1203.   for i := 0 to FNameList.count-1 do
  1204.   begin
  1205.     if (FNameList[i] = BinName) then
  1206.     begin
  1207.       pos := i;
  1208.       Result := True;
  1209.       Exit;
  1210.     end;
  1211.   end;
  1212. end;
  1213.  
  1214. function TBinData.GetIBinValue(iBin: Integer; ValueIndex: Integer): variant;
  1215. begin
  1216.   Result := GetBinValue(GetBinName(iBin), ValueIndex);
  1217. end;
  1218.  
  1219. function TBinData.GetIBinValueCount(iBin: Integer): Integer;
  1220. begin
  1221.   Result := BinValueCount(GetBinName(iBin));
  1222. end;
  1223.  
  1224. function TBinData.AddIBinValue(iBin: Integer; value: variant): Integer;
  1225. begin
  1226.   Result := AddBinValue(GetBinName(iBin), value);
  1227. end;
  1228.  
  1229. function HookToDataBase(aDataSet: TDataSet): TDataBase;
  1230. var
  1231.   aliasname: string;
  1232. begin
  1233.   Result := nil;
  1234.   if (aDataSet is TQuery) then
  1235.   begin
  1236.     Result := TQuery(aDataset).Database;
  1237.     if (Result = nil) then
  1238.     begin
  1239.       aliasname := TQuery(aDataSet).DataBaseName;
  1240.       if (aliasName = '') then Exit;
  1241.       Result := TQuery(aDataSet).DBSession.OpenDataBase(aliasName);
  1242.     end;
  1243.   end;
  1244. end;
  1245.  
  1246. end.
  1247.